// File: mpsc_ring_buffer.hpp
#pragma once

#include <atomic>
#include <cstddef>
#include <vector>

#include "macros.h"

namespace Common {

  /**
   * @brief 多生产者单消费者（MPSC）环形缓冲无锁队列。
   *
   *  - 模板参数 @tparam T 指定队列元素类型。
   *  - 通过序号（sequence）区分槽位状态，多个生产者竞争 tail 指针以入队。
   *  - 单消费者顺序推进 head 指针进行出队，无需额外同步。
   *
   * ⚠️ 设计注意：
   *  - 容量被提升至最接近的 2 次幂，便于使用掩码操作实现环绕。
   *  - 槽位序号的差值用于判断“当前槽位是否可写/可读”，需谨慎处理溢出。
   */
  template<typename T>
  class MPSCRingBuffer final {
  public:
    using value_type = T;

    explicit MPSCRingBuffer(std::size_t capacity)
        : capacity_(roundUpPow2(capacity)),
          mask_(capacity_ - 1),
          buffer_(capacity_) {
      ASSERT(capacity_ >= 2, "Capacity must be >= 2.");
      for (std::size_t i = 0; i < capacity_; ++i) {
        buffer_[i].sequence.store(i, std::memory_order_relaxed);
      }
    }

    /**
     * @brief 入队一个元素（多生产者并发调用）。
     *
     * @param value 待入队的数据。
     * @return 若成功写入返回 true；队列满返回 false。
     */
    bool enqueue(const T &value) noexcept {
      Slot *slot;
      std::size_t position = tail_.load(std::memory_order_relaxed);
      while (true) {
        slot = &buffer_[position & mask_];
        std::size_t seq = slot->sequence.load(std::memory_order_acquire);
        intptr_t diff = static_cast<intptr_t>(seq) - static_cast<intptr_t>(position);
        if (diff == 0) {
          if (tail_.compare_exchange_weak(position,
                                          position + 1,
                                          std::memory_order_relaxed,
                                          std::memory_order_relaxed)) {
            break;
          }
        } else if (diff < 0) {
          return false; // 队列已满
        } else {
          position = tail_.load(std::memory_order_relaxed);
        }
      }
      slot->value = value;
      slot->sequence.store(position + 1, std::memory_order_release);
      return true;
    }

    /**
     * @brief 出队一个元素（单消费者调用）。
     *
     * @param out_value 若成功出队，写入此引用。
     * @return 队列非空返回 true；否则 false。
     */
    bool dequeue(T &out_value) noexcept {
      Slot *slot;
      std::size_t position = head_.load(std::memory_order_relaxed);
      while (true) {
        slot = &buffer_[position & mask_];
        std::size_t seq = slot->sequence.load(std::memory_order_acquire);
        intptr_t diff = static_cast<intptr_t>(seq) - static_cast<intptr_t>(position + 1);
        if (diff == 0) {
          if (head_.compare_exchange_weak(position,
                                          position + 1,
                                          std::memory_order_relaxed,
                                          std::memory_order_relaxed)) {
            break;
          }
        } else if (diff < 0) {
          return false; // 队列为空
        } else {
          position = head_.load(std::memory_order_relaxed);
        }
      }
      out_value = slot->value;
      slot->sequence.store(position + mask_ + 1, std::memory_order_release);
      return true;
    }

    std::size_t capacity() const noexcept {
      return capacity_;
    }

  private:
    struct Slot {
      T value{};
      std::atomic<std::size_t> sequence{0};
    };

    static std::size_t roundUpPow2(std::size_t x) {
      std::size_t power_of_two = 1;
      while (power_of_two < x) {
        power_of_two <<= 1;
      }
      return power_of_two;
    }

    const std::size_t capacity_;
    const std::size_t mask_;
    std::vector<Slot> buffer_;

    std::atomic<std::size_t> head_{0}; ///< 单消费者读指针
    std::atomic<std::size_t> tail_{0}; ///< 多生产者写指针
  };

} // namespace Common